-- $Id: $
-- File name:   tb_AZALIA_BLOCK.vhd
-- Created:     4/21/2011
-- Author:      Brandon Blaine Gardner
-- Lab Section: 337-06
-- Version:     1.0  Initial Test Bench

--
-- This test bench tests the following items:
--
--   Valid SDO data from SRAM init (using C tool)
--   Valid SRAM dump from SDI data (using C tool)
--   Ability of device to transmit consecutive frames without pausing  ~21.2 us
--   Ability of device to detect dropped packets (Tx/Rx data not modified)  ~42 us
--   Ability of 'system' to write new Tx data to SRAM  ~15 us
--   Ability of 'system' to read new Rx data from SRAM  ~19 us
--
--
-- General test bench info:
--
--   This test bench sends and receives two full frames of IO data
--   A partial third frame is sent to verify the dropped packet errors
--   After the first frame, data is read by the system; this is not done after the second frame.  Serves as proof-of-concept only.
--   Uses mem_init_00.txt to initialize SRAM
--   Writes SDO for frame 1 to tb_output.txt; frame 2 to t2_output.txt
--   Reads SDI0 for frame 1 from tb_input.txt; frame 2 from t2_input.txt
--   SDI1 is (not SDI0) for all data except for null pads at end of data
--   


library ieee;
use ieee.std_logic_1164.all;
use IEEE.std_logic_textio.all;
--USE IEEE.std_logic_arith.ALL;
USE IEEE.std_logic_unsigned.ALL;
use std.textio.all;
library ECE337_IP;
use ECE337_IP.all;

entity tb_AZALIA_BLOCK is
generic (Period : Time := 10.416667 ns);
end tb_AZALIA_BLOCK;

architecture TEST of tb_AZALIA_BLOCK is

  function INT_TO_STD_LOGIC( X: INTEGER; NumBits: INTEGER )
     return STD_LOGIC_VECTOR is
    variable RES : STD_LOGIC_VECTOR(NumBits-1 downto 0);
    variable tmp : INTEGER;
  begin
    tmp := X;
    for i in 0 to NumBits-1 loop
      if (tmp mod 2)=1 then
        res(i) := '1';
      else
        res(i) := '0';
      end if;
      tmp := tmp/2;
    end loop;
    return res;
  end;

  component AZALIA_BLOCK
    PORT(
         CLK : IN std_logic;
         DATA_in : IN std_logic_vector (7 DOWNTO 0);
         RST : IN std_logic;
         Rx_DATA_STB : IN std_logic;
         SDI0 : IN std_logic;
         SDI1 : IN std_logic;
         Tx_DATA_STB : IN std_logic;
         ERR         : OUT    std_logic;
         CRIT        : OUT    std_logic;
         NEW_Rx_DATA : OUT    std_logic;
         REQ_Tx_DATA : OUT    std_logic;
         ADDR : OUT std_logic_vector (11 DOWNTO 0);
         BUSY : OUT std_logic;
         DATA_out : OUT std_logic_vector (7 DOWNTO 0);
         OWN_MEM : OUT std_logic;
         RE : OUT std_logic;
         RW : OUT std_logic;
         SDO : OUT std_logic;
         WE : OUT std_logic;
         SYNC : OUT std_logic
    );
  end component;
  
-- The declaration for the on-chip scalable sram model
  component scalable_off_chip_sram is
    generic (
          -- Memory Model parameters
          ADDR_SIZE_BITS  : natural  := 12;    -- Address bus size in bits/pins with addresses corresponding to 
                                               -- the starting word of the accesss
          WORD_SIZE_BYTES  : natural  := 1;      -- Word size of the memory in bytes
          DATA_SIZE_WORDS  : natural  := 1;      -- Data bus size in "words"
          READ_DELAY      : time    := 10 ns;  -- Delay/latency per read access (total time between start of supplying address and when the data read from memory appears on the r_data port)
                                               -- Keep the 10 ns delay for on-chip SRAM
          WRITE_DELAY      : time    := 10 ns    -- Delay/latency per write access (total time between start of supplying address and when the w_data value is written into memory)
                                                 -- Keep the 10 ns delay for on-chip SRAM
          );
    port  (
          -- Test bench control signals
          mem_clr        : in  boolean;
          mem_init      : in  boolean;
          mem_dump      : in  boolean;
          verbose        : in  boolean;
          init_filename  : in   string;
          dump_filename  : in   string;
          start_address  : in  natural;
          last_address  : in  natural;
          
          -- Memory interface signals
          r_enable  : in    std_logic;
          w_enable  : in    std_logic;
          addr      : in    std_logic_vector((addr_size_bits - 1) downto 0);
          data      : inout  std_logic_vector(((data_size_words * word_size_bytes * 8) - 1) downto 0)
        );
  end component scalable_off_chip_sram;

-- Insert signals Declarations here
  signal CLK : std_logic;
  signal DATA_in : std_logic_vector (7 DOWNTO 0);
  signal RST : std_logic;
  signal Rx_DATA_STB : std_logic;
  signal SDI0 : std_logic;
  signal SDI1 : std_logic;
  signal Tx_DATA_STB : std_logic;
  signal ADDR : std_logic_vector (11 DOWNTO 0);
  signal BUSY : std_logic;
  signal DATA_out : std_logic_vector (7 DOWNTO 0);
  signal OWN_MEM : std_logic;
  signal RE : std_logic;
  signal RW : std_logic;
  signal SDO : std_logic;
  signal WE : std_logic;
  signal ERR         : std_logic;
  signal CRIT        : std_logic;
  signal NEW_Rx_DATA : std_logic;
  signal REQ_Tx_DATA : std_logic;
  signal SYNC : std_logic;

-- SRAM signals
  signal tb_data : std_logic_vector(7 downto 0);
  
  signal tb_mem_clr : boolean;
  signal tb_mem_init : boolean;
  signal tb_mem_dump : boolean;
  signal tb_verbose : boolean;
  signal tb_init_filename : string(23 downto 1);
  signal tb_dump_filename : string(23 downto 1);
  signal tb_start_address : natural;
  signal tb_last_address : natural;
  
  signal tb_RW : std_logic;
  signal sys_RW : std_logic;
  signal tb_ADDR : std_logic_vector(11 downto 0);
  signal sys_ADDR : std_logic_vector(11 downto 0);
  signal tb_RE : std_logic;
  signal tb_WE : std_logic;
  signal sys_RE : std_logic;
  signal sys_WE : std_logic;
  
  signal tb_DATA_in : std_logic_vector(7 downto 0);
  signal tb_DATA_out : std_logic_vector(7 downto 0);
  signal sys_DATA_in : std_logic_vector(7 downto 0);
  signal sys_DATA_out : std_logic_vector(7 downto 0);
  

-------- < Signal for READING PROCESS >---------------
--signal data_in : std_logic_vector(499 downto 0);               --data read from the file.
------------------------------------------------------

-------- < Signal for WRITING PROCESS >---------------
--signal data_out : std_logic_vector(499 downto 0);        --data to be saved into the output file.
------------------------------------------------------

begin

CLKGEN: process
  variable CLK_tmp: std_logic := '0';
begin
  CLK_tmp := not CLK_tmp;
  CLK <= CLK_tmp;
  wait for Period/2;
end process;

  DUT: AZALIA_BLOCK port map(
                CLK => CLK,
                DATA_in => DATA_in,
                RST => RST,
                Rx_DATA_STB => Rx_DATA_STB,
                SDI0 => SDI0,
                SDI1 => SDI1,
                Tx_DATA_STB => Tx_DATA_STB,
                ADDR => ADDR,
                BUSY => BUSY,
                DATA_out => DATA_out,
                OWN_MEM => OWN_MEM,
                RE => RE,
                RW => RW,
                SDO => SDO,
                WE => WE,
                ERR => ERR,
                CRIT => CRIT,
                NEW_Rx_DATA => NEW_Rx_DATA,
                REQ_Tx_DATA => REQ_Tx_DATA,
                SYNC => SYNC
                );
                
-- map an instance of the on-chip scalable sram model
  Memory: scalable_off_chip_sram
    port map  (
                -- Test bench control signals
                mem_clr        => tb_mem_clr,
                mem_init      => tb_mem_init,
                mem_dump      => tb_mem_dump,
                verbose        => tb_verbose,
                init_filename  => tb_init_filename,
                dump_filename  => tb_dump_filename,
                start_address  => tb_start_address,
                last_address  => tb_last_address,
                
                -- Memory interface signals
                r_enable  => tb_RE,
                w_enable  => tb_WE,
                addr      => tb_ADDR,
                data      => tb_data
              );

-- connect up to the RW mux
  RW_MUX : process( OWN_MEM, sys_RW, RW)
  begin
    if( OWN_MEM = '0' )
    then
      tb_RW <= sys_RW;
    else
      tb_RW <= RW;
    end if;    
  end process RW_MUX;
  
  process( OWN_MEM, sys_RE, RE )
  begin
    if( OWN_MEM = '0' )
    then
      tb_RE <= sys_RE;
    else
      tb_RE <= RE;
    end if;
  end process;
  
  process( OWN_MEM, sys_WE, WE )
  begin
    if( OWN_MEM = '0' )
    then
      tb_WE <= sys_WE;
    else
      tb_WE <= WE;
    end if;
  end process;
  
  sys_DATA_in <= tb_DATA_in;
  DATA_in <= tb_DATA_in;
  
  process( OWN_MEM, sys_DATA_in, DATA_in, sys_DATA_out, DATA_out )
  begin
    if( OWN_MEM = '0' )
    then
      tb_DATA_out <= sys_DATA_out;
    else
      tb_DATA_out <= DATA_out;
    end if;
  end process;
  
-- connect up to the bidirectional data bus
  IO_DATA: process ( tb_RW, tb_data, tb_DATA_out )
  begin
    if (tb_RW = '1') then
      -- Read mode -> the data pins should connect to the r_data bus & the other bus should float
      tb_DATA_in  <= tb_data;
      tb_data  <= (others=>'Z');
    elsif(tb_RW = '0') then
      -- Write mode -> the data pins should connect to the w_data bus & the other bus should float
      tb_DATA_in  <= (others=>'Z');
      tb_data  <= tb_DATA_out;
    else
      -- Disconnect both busses
      tb_DATA_in  <= (others=>'Z');
      tb_data  <= (others=>'Z');
    end if;
  end process IO_DATA;
  
-- connect up to the address bus mux
  IO_ADDR: process( sys_ADDR, ADDR, OWN_MEM )
  begin
    if( OWN_MEM = '0' )
    then
      tb_ADDR <= sys_ADDR;
    else
      tb_ADDR <= ADDR;
    end if;
  end process IO_ADDR;


reading :
  process
      file in_file : text open read_mode is "test_io/tb_input.txt"; --declare input file
      file in_file2 : text open read_mode is "test_io/t2_input.txt"; -- infile 2
      variable in_line : line;                                  --declare line number
      variable data_read : std_logic_vector(499 downto 0);      --data read from the file.
      variable c1 : std_logic;                                  --temporary place for data after its read
    
  begin
  --  wait for Period*2;    --If we need it
    
    Rx_DATA_STB <= '0';
    Tx_DATA_STB <= '0';
    
    --==========================================
    
    wait until SYNC'event and SYNC = '1';
    wait until SYNC'event and SYNC='0';
    wait for Period*2;
    
    while not endfile(in_file) loop                             --checking if the "END OF FILE" is reached
      readline(in_file, in_line);                               --reads a line from the file
      read(in_line, c1);                                        --reads bit on the line
      SDI0 <= c1;
      SDI1 <= not c1;
      wait for Period*4;
    end loop;
      
    SDI0 <= '0';
    SDI1 <= '0';
    
    wait until SYNC'event and SYNC='1';
    -- START DUMP SRAM DATA
    tb_mem_dump       <= TRUE;
    tb_dump_filename  <= "test_io/mem_dump_00.txt";
    tb_start_address  <= 0; 
    tb_last_address   <= 383;
    report "Memory Dumped";
    wait for Period;
    tb_mem_dump       <= FALSE;
    -- END DUMP SRAM DATA
      
    --=============================================
    
    wait until SYNC'event and SYNC='0';
    wait for Period*2;
    
    while not endfile(in_file2) loop                             --checking if the "END OF FILE" is reached
      readline(in_file2, in_line);                               --reads a line from the file
      read(in_line, c1);                                        --reads bit on the line
      SDI0 <= c1;
      SDI1 <= not c1;
      wait for Period*4;
    end loop;
      
    SDI0 <= '0';
    SDI1 <= '0';
    
    wait until SYNC'event and SYNC='1';
    -- START DUMP SRAM DATA
    tb_mem_dump       <= TRUE;
    tb_dump_filename  <= "test_io/mem_dump_01.txt";
    tb_start_address  <= 0; 
    tb_last_address   <= 383;
    report "Memory Dumped";
    wait for Period;
    tb_mem_dump       <= FALSE;
    -- END DUMP SRAM DATA
    
    --===============================================
    
    wait;
                         
  end process reading;

writing :
  process
    
    file out_file : text open write_mode is "test_io/tb_output.txt";       --declare output file
    file out_file2 : text open write_mode is "test_io/t2_output.txt";       --declare output file
    variable out_line : line;                                      --declare line number 
    
  begin
  
  --===========================================
    
  wait until SYNC'event and SYNC = '1';
  wait until SYNC'event and SYNC = '0'; 
  wait for Period*3;
  
  for I in 679 downto 0 loop           --go through every bit in the signal
    
    write(out_line, SDO, RIGHT, 1);    --write bit to a line(linenumber,value,justified,field);
    writeline(out_file, out_line);     --write line to external file.
    --report "Sample";
    wait for Period*2;
      
  end loop;

  --=============================================
  
  wait until SYNC'event and SYNC = '1';
  wait until SYNC'event and SYNC = '0'; 
  wait for Period*3;
  
  for I in 679 downto 0 loop           --go through every bit in the signal
    
    write(out_line, SDO, RIGHT, 1);    --write bit to a line(linenumber,value,justified,field);
    writeline(out_file2, out_line);     --write line to external file.
    --report "Sample";
    wait for Period*2;
      
  end loop;
  
  --==============================================
      
end process writing;


sysram : -- emulates the system reading from and writing to SRAM for new data
process -- this is done only once as proof-of-concept
  
  -- variables/signals
  
begin
  
  sys_ADDR <= "000000000000";
  sys_RW <= '0';
  sys_RE <= '0';
  sys_WE <= '0';
  sys_DATA_out <= x"00";
  
  wait until REQ_Tx_DATA'event and REQ_Tx_DATA='1';
  
  report "Found Tx REQ";
  
  wait for 6 ns;
  
  for I in 0 to 4 loop
    
    if( BUSY = '1' )
    then
      wait until BUSY='0';
      wait for 6 ns;
    end if;
    
    sys_ADDR <= "000000000000" + I;
    sys_RE <= '1';
    sys_RW <= '1';
    wait for 10 ns;
    -- write data to file
    wait for 2 ns;
    sys_RW <= '0';
    sys_RE <= '0';
    
  end loop;
  
  for I in 127 to 158 loop
      
    if( BUSY = '1' )
    then
      wait until BUSY='0';
      wait for 6 ns;
    end if;
    
    sys_ADDR <= "000000000000" + I;
    sys_RE <= '1';
    sys_RW <= '1';
    wait for 10 ns;
    -- write data to file
    wait for 2 ns;
    sys_RW <= '0';
    sys_RE <= '0';
    
  end loop;
    
  for I in 191 to 238 loop
      
    if( BUSY = '1' )
    then
      wait until BUSY='0';
      wait for 6 ns;
    end if;
    
    sys_ADDR <= "000000000000" + I;
    sys_RE <= '1';
    sys_RW <= '1';
    wait for 10 ns;
    -- write data to file
    wait for 2 ns;
    sys_RW <= '0';
    sys_RE <= '0';
    
  end loop;
  
  Tx_DATA_STB <= '1'; -- equals 0 for error
  wait for 12 ns;
  Tx_DATA_STB <= '0';
  
  wait until NEW_Rx_DATA'event and NEW_Rx_DATA='1';
  
  report "Found Rx REQ"; 
  
  wait for 6 ns;
  
  for I in 63 to 72 loop
    
    if( BUSY = '1' )
    then
      wait until BUSY='0';
      wait for 6 ns;
    end if;
    
    sys_ADDR <= "000000000000" + I;
    sys_RE <= '1';
    sys_RW <= '1';
    wait for 10 ns;
    -- write data to file
    wait for 2 ns;
    sys_RW <= '0';
    sys_RE <= '0';
    
  end loop;
  
  for I in 255 to 302 loop
    
    if( BUSY = '1' )
    then
      wait until BUSY='0';
      wait for 6 ns;
    end if;
    
    sys_ADDR <= "000000000000" + I;
    sys_RE <= '1';
    sys_RW <= '1';
    wait for 10 ns;
    -- write data to file
    wait for 2 ns;
    sys_RW <= '0';
    sys_RE <= '0';
    
  end loop;
  
  for I in 319 to 366 loop
    
    if( BUSY = '1' )
    then
      wait until BUSY='0';
      wait for 6 ns;
    end if;
    
    sys_ADDR <= "000000000000" + I;
    sys_RE <= '1';
    sys_RW <= '1';
    wait for 10 ns;
    -- write data to file
    wait for 2 ns;
    sys_RW <= '0';
    sys_RE <= '0';
    
  end loop; 
  
  Rx_DATA_STB <= '1'; -- equals 0 for error
  wait for 12 ns;
  Rx_DATA_STB <= '0';
  
  wait;  
    
end process sysram;
  


 
main : process

  begin

    RST <= '0';
    
    -- init the memory contents from file
    tb_mem_init        <= TRUE;
    tb_init_filename   <= "test_io/mem_init_00.txt";
    wait for Period * 6;
    tb_mem_init        <= FALSE;
    
    RST <= '1';
    
    wait;
    
  end process main;
  
end TEST;
